package com.irdstudio.tdp.executor.core.plugin.util.bean;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * <p>复制并替换目录与文件</p>
 * @author caoxin && ligm
 * @date 2019-06-13
 */

public class ReplaceDirAndFileBean {

	private static Logger logger = LoggerFactory.getLogger(ReplaceDirAndFileBean.class);

	/** 原路径内容 */
	private List<String> srcStrList = new ArrayList<>();
	/** 新路径替换内容 */
	private List<String> outStrList = new ArrayList<>();
	/** 文件内容多组特换，用于指定多个替换模式 */
	private List<FileReplacePattern> replacePatternList = null;
	/** 源目录 */
	private String srcFilePath;
	/** 目标目录 */
	private String outFilePath;
	/** 默认查询值 */
	private String defFindStr;
	/** 默认替换值 */
	private String defReplaceStr;
	/** 需要忽略的后缀，即只复制不替换*/
	private String[] ignorePostfixArray;
	
	/**
	 * 构造函数
	 * @param srcFilePath
	 * @param outFilePath
	 * @param defFindStr
	 * @param defReplaceStr
	 */
	public ReplaceDirAndFileBean(String srcFilePath,String outFilePath,String defFindStr,String defReplaceStr) {
		this.srcFilePath = srcFilePath;
		this.outFilePath = outFilePath;
		this.defFindStr = defFindStr;
		this.defReplaceStr = defReplaceStr;
	}
	
	/**
	 * 增加替换模式
	 * @param pattern
	 */
	public void addFileReplacePattern(FileReplacePattern pattern) {
		if(this.replacePatternList == null) {
			this.replacePatternList = new ArrayList<FileReplacePattern>();
		}
		this.replacePatternList.add(pattern);
	}
	
	/**
	 * 执行函数
	 */
	public void execute() {
		
		logger.info("执行替换,源目录:{},输出目录:{}", this.srcFilePath, this.outFilePath);
		
		if (Objects.nonNull(srcFilePath) && Objects.nonNull(outFilePath) && Objects.nonNull(defFindStr)
				&& Objects.nonNull(defReplaceStr)) {
			
			// 分割用于处理文件路径
			String[] srcStrArr = defFindStr.split("\\.");
			String[] outStrArr = defReplaceStr.split("\\.");
			srcStrList = Arrays.asList(srcStrArr);
			outStrList = Arrays.asList(outStrArr);

			// 生成目录
			changeAndGeneratorFile(srcFilePath, outFilePath, defFindStr, defReplaceStr);
		} else {
			throw new RuntimeException("参数输入不正确");
		}
	}

	/**
	 * 入口函数
	 * 
	 * @param srcDir
	 *            源文件目录
	 * @param outDir
	 *            目标文件目录
	 * @param srcStr
	 *            需要替代值
	 * @param replaceStr
	 *            替代值
	 * @throws IOException
	 */
	public void changeAndGeneratorFile(String srcDir, String outDir, String srcStr, String replaceStr) {
		// 源文件夹
		File srcFile = new File(srcDir);
		// 目标文件夹
		File outFile = new File(outDir);
		if (srcFile.exists()) {
			if (!outFile.exists()) {
				outFile.mkdirs();
			}
			// 递归遍历并替换文件
			searchFile(srcFile, srcStr, replaceStr);
		}

	}

	/**
	 * 递归遍历文件夹
	 * 
	 * @param file
	 *            源文件或目录
	 * @param srcStr
	 *            需要代替的值
	 * @param replaceStr
	 *            代替的值
	 * @throws IOException
	 */
	private void searchFile(File file, String srcStr, String replaceStr) {
		// 文件存在
		if (file.exists()) {
			// 处理文件夹
			if (file.isDirectory()) {
				File[] files = file.listFiles();
				for (File srcFile : files) {
					if (srcFile.isDirectory()) {
						// 创建文件夹，递归
						createDir(srcFile, srcStr, replaceStr);
						searchFile(srcFile, srcStr, replaceStr);
					} else if (srcFile.isFile()) {
						// 创建文件
						createFile(srcFile, srcStr, replaceStr);
					}
				}
			} else if (file.isFile()) { // 处理文件
				createFile(file, srcStr, replaceStr);
			}
		}
	}

	/**
	 * 获取旧文件并替代内容
	 * 
	 * @param srcFile
	 *            源文件
	 * @param srcStr
	 *            需要替代的值
	 * @param replaceStr
	 *            替代值
	 * @return
	 */
	private StringBuffer replaceSrcFileContent(File srcFile, String srcStr, String replaceStr) {
		
		logger.info("替换{}文件内容...", srcFile.getAbsolutePath());
		
		StringBuffer strBuffer = null;
		try (BufferedReader bufReader = new BufferedReader(new InputStreamReader(new FileInputStream(srcFile)))) {
			// 数据流读取文件
			strBuffer = new StringBuffer();
			for (String temp = null; (temp = bufReader.readLine()) != null; temp = null) {
				// 处理默认替换
				if (temp.indexOf(srcStr) != -1) {
					temp = temp.replace(srcStr, replaceStr);
				}
				// 处理额外指定的替换，根据FileReplacePattern的值来进行替换
				if (this.replacePatternList != null) {
					for (FileReplacePattern frp : this.replacePatternList) {
						if ("*".equals(frp.getFilePattern()) || srcFile.getName().contains(frp.getFilePattern())) {
							if (temp.indexOf(frp.getFindValue()) != -1) {
								temp = temp.replace(frp.getFindValue(), frp.getReplaceValue());
							}
						}
					}
				}
				strBuffer.append(temp);
				strBuffer.append(System.getProperty("line.separator"));
			}
			bufReader.close();
		} catch (FileNotFoundException e) {
			String msg = "文件不存在：" + e;
			System.out.println(msg);
		} catch (IOException e) {
			String msg = "IO出现异常：" + e;
			System.out.println(msg);
		}
		return strBuffer;
	}

	/**
	 *
	 * @param newFilePath
	 * @param strBuffer
	 */
	private void writeToNewFile(String newFilePath, StringBuffer strBuffer) {
		try {
			// 如果文件不存在则创建文件
			File file = new File(newFilePath);
			if (!file.exists()) {
				file.createNewFile();
			}
			// 输出文件
			PrintWriter printWriter = new PrintWriter(file);
			printWriter.write(strBuffer.toString().toCharArray());
			printWriter.flush();
			printWriter.close();
		} catch (IOException e) {
			String msg = "输入文件出现异常" + e;
			System.out.println(msg);
		}
	}

	/**
	 * 创建替代文件
	 * 
	 * @param srcFile
	 *            源文件
	 * @param srcStr
	 *            需要代替的值
	 * @param replaceStr
	 *            代替值
	 * @throws IOException
	 */
	private void createFile(File srcFile, String srcStr, String replaceStr) {
		
		// 处理新文件路径并生成新文件
		String newFilePath = handleReplaceName(srcFile, srcStr, replaceStr);

		// 二进制文件或匹配忽略后缀的文件直接复制,除此之外的一律执行替换动作
		if (!isBinary(srcFile) && !isIgnorePostfix(srcFile)) {
			// 处理旧文件内容
			StringBuffer strBuffer = replaceSrcFileContent(srcFile, srcStr, replaceStr);
			writeToNewFile(newFilePath, strBuffer);
		} else {
			logger.info("二进制文件:{}", srcFile.getAbsolutePath());
			try {
				FileUtils.copyFile(srcFile, new File(newFilePath));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

	/**
	 * 判断是否为需要忽略的后缀
	 * @param srcFile
	 * @return
	 */
	private boolean isIgnorePostfix(File srcFile) {
		if (null == ignorePostfixArray) {
			return false;
		}
		String extension = FilenameUtils.getExtension(srcFile.getName());
		for (int i = 0; i < ignorePostfixArray.length; i++) {
			if (extension.equals(ignorePostfixArray[i])) {
				return true;
			}
		}
		return false;
	}

	/**
	 * 创建文件夹
	 * 
	 * @param srcFile
	 *            源文件
	 * @param srcStr
	 *            需要代替的值
	 * @param replaceStr
	 *            代替值
	 */
	public void createDir(File srcFile, String srcStr, String replaceStr) {
		// 处理文件路径（将路径中的值进行替换)
		String newFilePath = handleReplaceName(srcFile, srcStr, replaceStr);
		logger.info("新文件夹路径：{}", newFilePath);
		File directory = new File(newFilePath);
		if (srcFile.isDirectory()) {
			directory.mkdirs();
		}
	}

	/**
	 * 处理文件或文件路径中的名字
	 * 
	 * @param srcFile
	 *            源文件
	 * @param srcStr
	 *            需要代替的值
	 * @param replaceStr
	 *            代替值
	 * @return
	 */
	public String handleReplaceName(File srcFile, String srcStr, String replaceStr) {
		String filePath = srcFile.getAbsolutePath();
		filePath = filePath.replace(this.srcFilePath,this.outFilePath);
		// 处理文件名
		filePath = filePath.replace(srcStr, replaceStr);
		// 处理文件路径
		for (String str : srcStrList) {
			if (filePath.contains(str) && outStrList.size() > srcStrList.indexOf(str)) {
				filePath = filePath.replace(str, outStrList.get(srcStrList.indexOf(str)));
			}
		}
		return filePath;
	}
	
	/**
	 * 判断文件是否为二进制文件
	 * @param file
	 * @return
	 */
	public boolean isBinary(File file) {
		boolean isBinary = false;
		FileInputStream fin = null;
		try {
			fin = new FileInputStream(file);
			long len = file.length();
			for (int j = 0; j < (int) len; j++) {
				int t = fin.read();
				if (t < 32 && t != 9 && t != 10 && t != 13) {
					isBinary = true;
					break;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return isBinary;
	}

	/**
	 * 设置需要忽略的后缀
	 * @param string
	 */
	public void setIgnorePostfix(String ignorePostfix) {
		this.ignorePostfixArray = ignorePostfix.split(",");	
	}

}
